package com.example.educationblockchain.service;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.educationblockchain.mapper.BlockMapper;
import com.example.educationblockchain.mapper.StudentLoginMapper;
import com.example.educationblockchain.model.*;
import com.example.educationblockchain.util.BlockCache;
import com.example.educationblockchain.util.BlockConstant;
import com.example.educationblockchain.util.Encrypted;
import com.example.educationblockchain.util.SseEmitterServer;
import com.example.educationblockchain.websocket.P2PClient;
import com.example.educationblockchain.websocket.P2PServer;
import org.java_websocket.WebSocket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.spec.InvalidKeySpecException;
import java.util.*;

@Service
public class P2PService {

    @Autowired
    BlockCache blockCache;

    @Autowired
    BlockMapper blockMapper;

    @Autowired
    SseEmitterServer sseEmitterServer;

    @Autowired
    DataBlockService dataBlockService;

    @Autowired
    StudentLoginMapper studentLoginMapper;

    Encrypted encrypted = new Encrypted();

    public List<WebSocket> getSockets(){
        return blockCache.getSocketsList();
    }

    public String getPeerPassword(){
        return blockCache.getPassword();
    }

    public void handleMessage(WebSocket webSocket, String msg, List<WebSocket> sockets) {
        try {
            Message message = JSON.parseObject(msg, Message.class);
            System.out.println("接收到IP地址为：" +webSocket.getRemoteSocketAddress().getAddress().toString()
                    +"，端口号为："+ webSocket.getRemoteSocketAddress().getPort() + "的p2p消息");
            switch (message.getType()) {
                case BlockConstant.P2P_TEST1:
                    System.out.println(message.getData());
                    break;
                case BlockConstant.PEER_CONNECT:
                    if(!message.getData().equals(blockCache.getPassword())){
                        webSocket.close();
                        System.out.println("密码错误");
                    }
                    blockCache.setPassword(Encrypted.getRandomString(16));
                    break;
                // 服务端接收客户端请求最新区块的请求:1
                case BlockConstant.QUERY_LATEST_BLOCK:
                    System.out.println("服务端调用方法返回最新区块");
                    write(webSocket, responseLatestBlockMsg());
                    break;
                // 客户端接收服务端发送的最新区块:2
                case BlockConstant.RESPONSE_LATEST_BLOCK:
                    System.out.println("接收到服务端返回的最新区块");
                    handleBlockResponse(message.getData(), webSocket);
                    break;
                // 服务端接收客户端请求指定区块的请求:3
                case BlockConstant.QUERY_BLOCK:
                    System.out.println("服务端调用方法返回指定区块");
                    write(webSocket, responseBlockMsg(message.getData()));
                    break;
                // 客户端接收服务端发送的发送指定区块:4
                case BlockConstant.RESPONSE_BLOCK:
                    System.out.println("接收到服务端发送的发送的指定区块");
                    handleBlock(message.getData());
                    break;
                // 服务端接收客户端sse请求指定区块的请求:5
                case BlockConstant.Sse_QUERY_BLOCK:
                    System.out.println("服务端调用方法返回Sse指定区块");
                    write(webSocket, responseSseBlockMsg(message.getData()));
                    break;
                // 客户端sse接收服务端发送的指定区块:6
                case BlockConstant.Sse_RESPONSE_BLOCK:
                    System.out.println("接收到服务端发送的发送的Sse指定区块");
                    handleSseBlock(message.getData());
                    break;
                // 客户端处理错误区块:7
                case BlockConstant.ERROR_BLOCK:
                    System.out.println("接收到服务端发送的发送的错误区块信息");
                    handleErrorBlock(message.getData());
                    break;
            }
        } catch (Exception e) {
            System.out.println("处理IP地址为：" +webSocket.getRemoteSocketAddress().getAddress().toString()
                    +"，端口号为："+ webSocket.getRemoteSocketAddress().getPort() + "的p2p消息错误:"
                    + e.getMessage());
        }
    }

    public String responseLatestBlockMsg() {
        Message msg = new Message();
        msg.setType(BlockConstant.RESPONSE_LATEST_BLOCK);

        List<String> list = new ArrayList<>();
        long index = blockMapper.selectBlockNum();
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("_index",index-1);
        Block block = blockMapper.selectOne(wrapper);


        list.add(JSON.toJSONString(block));
        list.add(JSON.toJSONString(dataBlockService.findDataBlockByHash(block.getHash()).get(0)));

        msg.setData(JSON.toJSONString(list));
        return JSON.toJSONString(msg);
    }

    public String responseSseBlockMsg(String msgData) {
        Message msg = new Message();
        msg.setType(BlockConstant.Sse_RESPONSE_BLOCK);

        List<String> data = new ArrayList<>();

        List<String> list = JSON.parseArray(msgData,String.class);

        long index = Long.parseLong(list.get(0));

        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("_index",index);
        Block block = blockMapper.selectOne(wrapper);

        data.add(JSON.toJSONString(block));
        data.add(JSON.toJSONString(dataBlockService.findDataBlockByHash(block.getHash()).get(0)));
        data.add(list.get(1));

        msg.setData(JSON.toJSONString(data));

        return JSON.toJSONString(msg);
    }

    public String responseBlockMsg(String id) {
        Message msg = new Message();
        msg.setType(BlockConstant.RESPONSE_BLOCK);

        List<String> list = new ArrayList<>();
        long index = Long.parseLong(id);

        System.out.println("发送第"+index+"个区块");

        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("_index",index);
        Block block = blockMapper.selectOne(wrapper);


        list.add(JSON.toJSONString(block));
        list.add(JSON.toJSONString(dataBlockService.findDataBlockByHash(block.getHash()).get(0)));

        msg.setData(JSON.toJSONString(list));
        return JSON.toJSONString(msg);
    }

    public void responseNewBlockMsg(Block block,DataBlock dataBlock) {
        Message msg = new Message();
        msg.setType(BlockConstant.RESPONSE_LATEST_BLOCK);

        System.out.println("增加index为"+block.getIndex()+"的区块");

        List<String> list = new ArrayList<>();


        list.add(JSON.toJSONString(block));
        list.add(JSON.toJSONString(dataBlock));

        msg.setData(JSON.toJSONString(list));

        broatcast(JSON.toJSONString(msg));
    }

    public synchronized void handleBlockResponse(String msgData, WebSocket socket) {
        //反序列化得到其它节点的最新区块信息
        List<String> list = JSON.parseArray(msgData,String.class);

        Block block = JSON.parseObject(list.get(0),Block.class);
        DataBlock dataBlock = JSON.parseObject(list.get(1),DataBlock.class);


        long index = blockMapper.selectBlockNum();
        System.out.println("index大小："+index);

        if(block.getIndex()==index){
            System.out.println("111111111111111111111111111");
            blockMapper.insert(block);
            dataBlockService.saveDataBlock(dataBlock);
            QueryWrapper wrapper = new QueryWrapper();
            wrapper.eq("id",block.getNumber());
            if(studentLoginMapper.selectList(wrapper).isEmpty()){
                StudentLogin studentLogin = new StudentLogin(block.getNumber(),block.getNumber());
                studentLoginMapper.insert(studentLogin);
            }
        }else if(block.getIndex()>index){
            System.out.println("2222222222222222222222222222");
            Message msg = new Message();
            msg.setType(BlockConstant.QUERY_BLOCK);

            for(long i = index;i<=block.getIndex();i++){
                msg.setData(i+"");
                broatcast(JSON.toJSONString(msg));
                System.out.println("广播请求第"+i+"条信息");
            }
            msg.setType(BlockConstant.QUERY_LATEST_BLOCK);
            msg.setData("请求最新区块");
            broatcast(JSON.toJSONString(msg));
        }else{
            System.out.println("333333333333333333333333333");
            QueryWrapper wrapper = new QueryWrapper();
            wrapper.eq("_index",block.getIndex());
            if(!block.equals(blockMapper.selectOne(wrapper))){
                Message msg = new Message();

                msg.setType(BlockConstant.ERROR_BLOCK);
                msg.setData(block.getIndex()+"");
                write(socket,JSON.toJSONString(msg));
            }
        }

    }

    public void handleBlock(String msgData) {
        //反序列化得到其它节点的最新区块信息
        List<String> list = JSON.parseArray(msgData,String.class);

        Block block = JSON.parseObject(list.get(0),Block.class);
        DataBlock dataBlock = JSON.parseObject(list.get(1),DataBlock.class);


        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("_index", block.getIndex());
        if(blockMapper.selectOne(wrapper)!=null){
            System.out.println("啊啊啊啊啊啊啊啊");
            blockMapper.update(block,wrapper);
            dataBlockService.updateDataBlock(dataBlock);
        }else {
            blockMapper.insert(block);
            dataBlockService.saveDataBlock(dataBlock);
        }

        wrapper.clear();
        wrapper.eq("id",block.getNumber());
        if(studentLoginMapper.selectList(wrapper).isEmpty()){
            StudentLogin studentLogin = new StudentLogin(block.getNumber(),block.getNumber());
            studentLoginMapper.insert(studentLogin);
        }

    }

    public String handleErrorBlock(String index) {
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("_index",index);
        Block block = blockMapper.selectOne(wrapper);
        blockMapper.delete(wrapper);
        dataBlockService.deleteDataBlockByHash(block.getHash());

        Message msg = new Message();
        msg.setType(BlockConstant.QUERY_LATEST_BLOCK);
        msg.setData("请求最新区块");
        broatcast(JSON.toJSONString(msg));

        return "";
    }

    public synchronized void handleSseBlock(String msgData) throws BadPaddingException, NoSuchPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, UnsupportedEncodingException, NoSuchProviderException, InvalidKeyException, InvalidKeySpecException {
        //反序列化得到区块信息
        List<String> list = JSON.parseArray(msgData,String.class);

        Map<String,Object> result = new HashMap<>();

        Block block = JSON.parseObject(list.get(0),Block.class);
        DataBlock dataBlock = JSON.parseObject(list.get(1),DataBlock.class);
        String id=list.get(2);

        System.out.println(block);
        System.out.println(dataBlock);

        Data data = encrypted.Decrypt(block,dataBlock.getDataBlock());


        result.put("data",data);

        result.put("hash",block.getHash());

        result.put("index",block.getIndex());

        System.out.println(id);

        sseEmitterServer.sendMessage(id,result);
        sseEmitterServer.removeUser(id);
    }

    public void SseSend(Map<String,String> map){
        String address = map.get("peer");
        List<WebSocket> socketsList = this.getSockets();
        List<String> list = new ArrayList<>();

        Optional<WebSocket> socket = socketsList.stream()
                .filter(web -> address.equals(web.getRemoteSocketAddress().toString()))
                .findAny();

        if (socket.isPresent()){
            WebSocket ws = socket.get();
            System.out.println(ws.getRemoteSocketAddress().toString());

            Message msg = new Message();
            msg.setType(BlockConstant.Sse_QUERY_BLOCK);

            list.add(map.get("index"));
            list.add(map.get("userId"));

            msg.setData(JSON.toJSONString(list));

            this.write(ws,JSON.toJSONString(msg));
        }
    }

    public void getDataFromPeer(Map<String,String> map){
        String address = map.get("peer");
        long start = Long.parseLong(map.get("start"));
        long end = Long.parseLong(map.get("end"));
        List<WebSocket> socketsList = this.getSockets();
        List<String> list = new ArrayList<>();

        Optional<WebSocket> socket = socketsList.stream()
                .filter(web -> address.equals(web.getRemoteSocketAddress().toString()))
                .findAny();

        if (socket.isPresent()){
            WebSocket ws = socket.get();
            System.out.println(ws.getRemoteSocketAddress().toString());

            Message msg = new Message();
            msg.setType(BlockConstant.QUERY_BLOCK);

            for(Long i = start;i<=end;i++){
                msg.setData(i+"");
                broatcast(JSON.toJSONString(msg));
            }
        }
    }

    public void webSocketClose(String address){
        List<WebSocket> socketsList = this.getSockets();

        Optional<WebSocket> socket = socketsList.stream()
                .filter(web -> address.equals(web.getRemoteSocketAddress().toString()))
                .findAny();

        if (socket.isPresent()) {
            WebSocket ws = socket.get();
            ws.close();
        }
    }



    //全网广播消息
    public void broatcast(String message) {
        List<WebSocket> socketsList = this.getSockets();
        if (CollectionUtils.isEmpty(socketsList)) {
            return;
        }
        System.out.println("======全网广播消息开始：");
        for (WebSocket socket : socketsList) {
            this.write(socket, message);
        }
        System.out.println("======全网广播消息结束");
    }

    //向其它节点发送消息
    public void write(WebSocket ws, String message) {
        System.out.println("发送给IP地址为：" +ws.getRemoteSocketAddress().getAddress().toString()
                + "，端口号为："+ws.getRemoteSocketAddress().getPort() + " 的p2p消息");
        System.out.println(ws);
        ws.send(message);
    }

    public String queryLatestBlockMsg() {
        Message msg = new Message();
        msg.setType(BlockConstant.QUERY_LATEST_BLOCK);
        return JSON.toJSONString(msg);
    }

    public String connectNewPeer(String pwd) {
        Message msg = new Message();
        msg.setType(BlockConstant.PEER_CONNECT);
        msg.setData(pwd);
        return JSON.toJSONString(msg);
    }

    public void test(){
        Message msg = new Message();
        msg.setType(BlockConstant.P2P_TEST1);
        msg.setData("P2P连接测试");
        broatcast(JSON.toJSONString(msg));
    }

    public void test2(){
        Message msg = new Message();
        msg.setType(BlockConstant.PEER_CONNECT);
        msg.setData("websocket断开测试");
        broatcast(JSON.toJSONString(msg));
    }

    public void testP2P(){
        Message msg = new Message();
        msg.setType(BlockConstant.QUERY_LATEST_BLOCK);
        msg.setData("P2P传输测试");
        broatcast(JSON.toJSONString(msg));
    }


}
